/*

emuDOS Version 1.0

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
/*
This file ("main.c") is part of emuDOS, and serves as the main file for emuDOS.
*/
#include <stdio.h>
#include <termios.h> /* Used for no-echo input. int 21h/ah=07h */
#include "version.h"
#include "registers.h"
#define X86 0
#define X86_64 1
#define PPC 2
#define MIPS 3

int ppcRun(FILE *prog,int fs);

int main(int argc, char *argv[]) {
	if (argc < 2) {
		printf("emuDOS Version %d.%d.%d build %d - Copyright (C) 2011 by Matthew Abbey\n\
		Usage: emudos [filename] [-x arch]\n\n\
                filename        The name of the file to emulate.\n\
                -x arch         Run the file with the instruction set defined by arch.\n\
                                Valid values: x86 x86_64 mips ppc.\n\n\
                More help: http://code.google.com/p/emudos/\n", VER_MAJOR, VER_MINOR, VER_RELEASE, BUILD_NO);
	} else {
		FILE *exec;
		FILE *print; // Explained when you get to the print subfunction of the 21h interruption...
		int architecture = 0;
		int jmpLocation = 0;
		print = fopen("/tmp/emudos_print.prnout", "w+");
		exec = fopen(argv[1], "rb");
		if (exec == NULL) {
		  printf("Warning! Unable to open file %s.", argv[1]);
		  return 0;
		}
		// Time for the sizeof subroutine!
		int fileSize = 0;
		while (fgetc(exec) != EOF) {
			fileSize++;
		}
		fseek(exec, 0, SEEK_SET);
		unsigned char buffer[fileSize];
		int position = 0;
		int bCount = 0;
		if (argv[2] == "-x") {
			if(argv[2] == "x86"){
				architecture = X86;
			}
			if(argv[4] == "x86_64") {
				architecture = X86_64;
			}
			if(argv[4] == "ppc") {
				architecture = PPC;
			}
			if(argv[4] == "mips") {
				architecture = MIPS;
			}
		}
		if (architecture == PPC) { // Our architecture is of PowerPC. Apple has an advantage, because I found the opcodes for Apple's PPC before Motorolla's.
			ppcRun(exec, fileSize);
		}
		if (architecture == X86) {
		fread(buffer, 1, fileSize, exec);
		while(position < fileSize + 1) {
			// printf("The instruction at decimal offset %d is %d.",position, (int) buffer[position]); // Leave this for testing purposes!
			if (buffer[position] == 0xb0) { // MOV al - Accumulator low-part
			  position++;
			  al = buffer[position];
			}
			else if (buffer[position] == 0xb4) { // MOV ah - Accumulator high-part
			  position++;
			  ah = buffer[position];
			}
			else if (buffer[position] == 0xb8) { // MOV ax - 16-bit Accumulator
			  position++;
			  ax = buffer[position];
			}
			else if (buffer[position] == 0xb3) { // MOV bl - Base address low-part
			  position++;
			  bl = buffer[position];
			}
			else if (buffer[position] == 0xb7) { // MOV bh - Base address high-part
			  position++;
			  bl = buffer[position];
			}
			else if (buffer[position] == 0xbb) { // MOV bx - 16-bit Base address
			  position++;
			  bx = buffer[position];
			}
			else if (buffer[position] == 0xb1) { // MOV cl - Counter low-part
			  position++;
			  cl = buffer[position];
			}
			else if (buffer[position] == 0xb5) { // MOV ch - Counter high-part
			  position++;
			  cl = buffer[position];
			}
			else if (buffer[position] == 0xb9) { // MOV cx - 16-bit Counter
			  position++;
			  cx = buffer[position];
			}
			else if (buffer[position] == 0xb2) { // MOV dl - Data low-part
			  position++;
			  dl = buffer[position];
			}
			else if (buffer[position] == 0xb6) { // MOV dh - Data high-part
			  position++;
			  dh = buffer[position];
			}
			else if (buffer[position] == 0xba) { // MOV dx - 16-bit Data register
			  position++;
			  dx = buffer[position];
			}
			else if (buffer[position] == 0x66) { // MOV one of the 32-bit registers
			  position++;
			  if (buffer[position] == 0xb8) { // MOVing eax
			    position++;
			    eax = buffer[position];
			  }
			  else if (buffer[position] == 0xbb) { // MOVing ebx
			    position++;
			    ebx = buffer[position];
			  }
			  else if (buffer[position] == 0xb9) { // MOVing ecx
			    position++;
			    ecx = buffer[position];
			  }
			  else if (buffer[position] == 0xba) { // MOVing edx
			    position++;
			    edx = buffer[position];
			  }
			  else {
			    printf("\n\n Error! Unkown register referred to at decimal offset: %d!\n\n", position);
			  }
			}
			else if (buffer[position] == 0x05) { // ADD AX and a number...
			  position++;
			  ax = ax + buffer[position];
			}
			else if (buffer[position] == 0x81) { // ADD an immediate word to a word...
			  position++;
			  if (buffer[position] == 0xc3) { // We are adding bx and a number
			    position++;
			    bx = bx + buffer[position];
			  }
			  else if (buffer[position] == 0xc1) { // We are adding cx and a number
			    position++;
			    cx = cx + buffer[position];
			  }
			  else if (buffer[position] == 0xc2) { // We are adding dx and a number
			    position++;
			    cx = cx + buffer[position];
			  }
			  else {
			    printf("\nError! Unknown register referred to at decimal offset: %d in ADD instruction!\n", position);
			  }
			}
			else if (buffer[position] == 0x80) { // ADD an immediate byte to a byte...
			  position++;
			  if (buffer[position] == 0xc4) { // ADD ah and a number
			    position++;
			    ah = ah + buffer[position];
			  }
			  else if (buffer[position] == 0xc7) { // ADD bh and a number
			    position++;
			    bh = bh + buffer[position];
			  }
			  else if (buffer[position] == 0xc3) { // ADD bl and a number
			    position++;
			    bl = bl + buffer[position];
			  }
			  else if (buffer[position] == 0xc5) { // ADD ch and a number
			    position++;
			    ch = ch + buffer[position];
			  }
			  else if (buffer[position] == 0xc1) { // ADD cl and a number
			    position++;
			    cl = cl + buffer[position];
			  }
			  else if (buffer[position] == 0xc6) { // ADD dh and a number
			    position++;
			    dh = dh + buffer[position];
			  }
			  else if (buffer[position] == 0xc2) { // ADD dl and a number
			    position++;
			    dl = dl + buffer[position];
			  }
			  else {
			    printf("\n\n Error! Unknown register referred to at decimal offset: %d!\n\n", position);
			  }
			}
			else if (buffer[position] == 0xe9) { // JMP to displacement relative of next instruction...
			  position++;
			  jmpLocation = position + buffer[position];
			  position = jmpLocation;
			}
			else if (buffer[position] == 0xff) { // JMP to displacement relative of value of register.
			  position++;
			  if (buffer[position] == 0xe0) { // We are using AX
			    jmpLocation = ax + buffer[position];
			    position = jmpLocation;
			  }
			}
			else if (buffer[position] == 0x90) { // NOP instruction.
			  printf("\n\nReached NOP instruction at decimal offset: %d.\n", position);
			  return 0;
			}
			else if (buffer[position] == 0xc3) { // RET instruction
			  printf("\n\nProgram returned control to the operating system  at decimal offset: %d.\n", position);
			  return 0;
			}
			else if (buffer[position] == 0xf4) { // Halt!
			  printf("\n\nProgram halted at decimal offset: %d.\n", position);
			}
			else if (buffer[position] == 0x00) { // No instruction - break in between instructions.
			  bCount++;
			}
			else if (buffer[position] == 0xcd) { // Calling an interrupt...
			  position++;
			  /**
			    Begin Interrupt 21h.
			                       **/
			  if (buffer[position] == 0x21) { // The world-famous 21h DOS interrupt!
			    if (ah == 0x00) { // This does not exist!
			      printf("Error: invalid argument for interrupt 21h! (Decimal offset %d.)\n", position);
			    }
			    else if (ah == 0x01) { // Get character from STDIN
			      scanf("%d", &al);
			    }
			    else if (ah == 0x02) { // Put character to STDOUT
			      printf("%c", (char)dl);
			    }
			    else if (ah == 0x05) {
			      /* Because printing is too hard to do one character at a time with printers
			         these days, a file named emudos_print.prnout in the /var directory of Linux.
			      */
			      fputc(dl, print);
			    }
			    else if (ah == 0x06) { // Direct console input/output. Don't get why it's divided like this, though...
			      if (dl == 0xff) {
				al = 0x0;
				scanf("%d", &al);
				if (al == 0x0) {
				  al = 0x0;
				  zf = 0x1;
				}
				else {
				  zf = 0x0;
				}
			      }
			      else {
				printf("%c", (char)dl);
			      }
			    }
			    else if (ah == 0x07 || ah == 0x08) { // Get a character with no echo...
			      al = getpass("");
			    }
			    else if (ah == 0x09) {

			    }
			  }
			  /**
			    End Interrupt 21h.
			                     **/
			  if (buffer[position] == 0x10) { // The 10h graphics interrupt.
			    if (ah == 0x00) {
			      //setVideoMode(al);
			    }
			    if (ah == 0x01) {
			      // Can't do much with this yet.
			    }
			    if (ah == 0x02) {
			      /*asm("movl $0x02, %ah\n\t"
			          "movl %r, %dh\n\t"
			          "movl %c, %dl\n\t"
			          "int $0x10"
			          : // Nothing to output...
			          : "r" (dh), "r" (dl)
			          ); // The poor guy is lonely. =( */
			    }
			    if (ah == 0x03) {

			    }
			  }
			}
			else
			  printf("Error: Unkown instruction %d at decimal offset: %d.\n", buffer[position], position);
			position++;
		}
		}
	}
	return 0;
}
